using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Data;
using System.Net;
using System.Text;
using System.Threading;
using CodePlex.TfsLibrary.ObjectModel;
using CodePlex.TfsLibrary.RegistrationWebSvc;
using CodePlex.TfsLibrary.RepositoryWebSvc;
using CodePlex.TfsLibrary.Utility;
using Xunit;
using XunitExt;

namespace UnitTest.Integration
{
	public class WebTransferServiceTest
	{
		private readonly IWebTransferService webTransferService = new WebTransferService(new FileSystem());

		[Fact]
		public void CanDownloadFile()
		{
			byte[] bytes =
				webTransferService.DownloadBytes("http://www.codeplex.com",
												 System.Net.CredentialCache.DefaultCredentials);
			string codePlexContent = Encoding.UTF8.GetString(bytes);
			Assert.Contains("CodePlex", codePlexContent);
		}

		[Fact]
		public void CanDownloadFileAsync()
		{
			string codePlexContent = null;
			IAsyncResult asyncResult =
				webTransferService.BeginDownloadBytes("http://www.codeplex.com",
													  System.Net.CredentialCache.DefaultCredentials,
													  delegate(IAsyncResult ar)
													  {
														  byte[] bytes = webTransferService.EndDownloadBytes(ar);
														  codePlexContent = Encoding.UTF8.GetString(bytes);
													  });
			asyncResult.AsyncWaitHandle.WaitOne();
			Assert.Contains("CodePlex", codePlexContent);
		}

		[Fact]
		public void WillNotBlockIfThereIsErrorInStartingAsyncDownload()
		{
			string url = GenerateDownloadUrl(GenerateAnonymousCredentials(), "https://tfs01.codeplex.com/", "$/ValidationFramework/Validation/Examples/ExampleLibraryCSharp/BusinessBaseClasses/NotifyValidatableBaseSample.cs", 25578);

			IAsyncResult ar = webTransferService.BeginDownloadBytes(url, new NetworkCredential("bad", "user", "name"), null);
			ar.AsyncWaitHandle.WaitOne();
			Assert.Throws<UnauthorizedAccessException>(delegate
			{
				webTransferService.EndDownloadBytes(ar);
			});
		}


		[Fact]
		public void CanDownloadFileFromTfs_01()
		{
			CredentialCache cache = GenerateAnonymousCredentials();
			string url = GenerateDownloadUrl(cache, "https://tfs01.codeplex.com/", "$/ValidationFramework/Validation/Examples/ExampleLibraryCSharp/BusinessBaseClasses/NotifyValidatableBaseSample.cs", 25578);

			try
			{
				byte[] bytes = webTransferService.DownloadBytes(url, cache);
				Assert.Equal(5475, bytes.Length);
			}
			catch (WebException e)
			{
				throw new Exception(new StreamReader(e.Response.GetResponseStream()).ReadToEnd(), e);
			}
		}

		private CredentialCache GenerateAnonymousCredentials()
		{
			CredentialCache cache = new CredentialCache();
			cache.Add(new Uri("https://tfs01.codeplex.com/"), "Basic", new NetworkCredential("anonymous", null));
			cache.Add(new Uri("https://tfs03.codeplex.com/"), "Basic", new NetworkCredential("anonymous", null));
			return cache;
		}

		private string GenerateDownloadUrl(CredentialCache cache, string tfsUrl, string path, int version)
		{
			IRegistrationWebSvcFactory registrationWebSvcFactory = new RegistrationWebSvcFactory();
			SourceControlService scm = new SourceControlService(new RegistrationService(registrationWebSvcFactory), new RepositoryWebSvcFactory(registrationWebSvcFactory), null,new FileSystem());
			SourceItem[] items = scm.QueryItems(tfsUrl, cache,
			                                    path,
			                                    RecursionType.None, VersionSpec.FromChangeset(version), DeletedState.Any, ItemType.Any);
			return items[0].DownloadUrl;
		}

		[Fact]
		public void CanDownloadFileFromTfs_03()
		{
			string url = GenerateDownloadUrl(GenerateAnonymousCredentials(), "https://tfs03.codeplex.com/", 
				 "$/CodePlexClient/Source/CodePlexClient/HelpCommand.cs", 17000);
			byte[] bytes = webTransferService.DownloadBytes(url, GenerateAnonymousCredentials());
			Assert.Equal(885, bytes.Length);
		}

		[Fact]
		public void CanDownloadFileAsyncFromTfs_01()
		{
			CredentialCache cache = GenerateAnonymousCredentials();
			string url = GenerateDownloadUrl(cache, "https://tfs01.codeplex.com/",
				"$/ValidationFramework/Validation/Examples/ExampleLibraryCSharp/BusinessBaseClasses/NotifyValidatableBaseSample.cs", 25578);

			IAsyncResult asyncResult = webTransferService.BeginDownloadBytes(url, cache, null);
			asyncResult.AsyncWaitHandle.WaitOne();
			byte[] bytes = webTransferService.EndDownloadBytes(asyncResult);
			Assert.Equal(5475, bytes.Length);
		}

		[Fact]
		public void CanDownloadFileAsyncFromTfs_03()
		{
			string url = GenerateDownloadUrl(GenerateAnonymousCredentials(), "https://tfs03.codeplex.com/",
				 "$/CodePlexClient/Source/CodePlexClient/HelpCommand.cs", 17000);
			byte[] bytes = webTransferService.DownloadBytes(url, GenerateAnonymousCredentials());
			Assert.Equal(885, bytes.Length);
		}


		public static IEnumerable<object[]> Urls
		{
			get
			{
			    yield return new object[]
			                     {
			                         "http://www.codeplex.com"
			                     };
			    yield return new object[]
			                     {
			                         "http://www.codeplex.com/SvnBridge",
			                     };
                yield return new object[]
			                     {
			                   "http://www.codeplex.com/xunit",
					  };
			    yield return new object[]
			                     {
			                         "http://www.codeplex.com/CodePlexClient"
			                     };
			}
		}

		[Theory(Skip="Doesn't work now")]
        [PropertyData("Urls")]
		public void AsyncAndSyncDownloadsAreTheSame(string url)
		{
			string codePlexContentAsync = null;
			IAsyncResult asyncResult =
				webTransferService.BeginDownloadBytes(url, System.Net.CredentialCache.DefaultCredentials,
													  delegate(IAsyncResult ar)
													  {
														  byte[] bytesAsync = webTransferService.EndDownloadBytes(ar);
														  codePlexContentAsync = Encoding.UTF8.GetString(bytesAsync);
													  });
			byte[] bytes = webTransferService.DownloadBytes(url, System.Net.CredentialCache.DefaultCredentials);
			string codePlexContent = Encoding.UTF8.GetString(bytes);
			asyncResult.AsyncWaitHandle.WaitOne();
			Assert.Equal(codePlexContent, codePlexContentAsync);
		}

		[Fact]
		public void DownloadBigFile()
		{
			string url = "http://localhost:5421/";
			string filename = Path.Combine(Path.GetTempPath(), "test.txt");
			GenerateFile(filename);
			WithListener(filename, url, delegate
			{
				byte[] downloadedBytes = webTransferService.DownloadBytes(url, CredentialCache.DefaultCredentials);
				Assert.Equal(
					File.ReadAllBytes(filename),
					downloadedBytes
					);
			});
		}

		[Fact]
		public void DownloadFile_WhenThrowsException_WillReThrowExceptionOnEndDownloadBytes()
		{
			string url = "http://localhost:1/";
			bool callbackCalled = false;
			IAsyncResult asyncResult = webTransferService.BeginDownloadBytes(url, CredentialCache.DefaultCredentials, delegate(IAsyncResult ar)
			{
				Assert.Throws<WebException>(delegate
				{
					byte[] bytes = webTransferService.EndDownloadBytes(ar);
				});
				callbackCalled = true;
			});
			asyncResult.AsyncWaitHandle.WaitOne();
            Thread.Sleep(2000);
			Assert.True(callbackCalled);
		}

		[Fact]
		public void DownloadBigFile_Async()
		{
			string url = "http://localhost:5421/";
			string filename = Path.Combine(Path.GetTempPath(), "test.txt");
			GenerateFile(filename);
			WithListener(filename, url, delegate
			{
				IAsyncResult result = webTransferService.BeginDownloadBytes(url, CredentialCache.DefaultCredentials, delegate(IAsyncResult ar)
				{
					byte[] downloadedBytes = webTransferService.EndDownloadBytes(ar);
					Assert.Equal(
						File.ReadAllBytes(filename),
						downloadedBytes
						);
				});
				result.AsyncWaitHandle.WaitOne();
			});
		}

		private static void WithListener(string filename, string url, Action<int> action)
		{
			using (HttpListener listener = new HttpListener())
			{
				listener.Prefixes.Add(url);
				listener.Start();
				listener.BeginGetContext(delegate(IAsyncResult ar)
				{
					HttpListenerContext context = listener.EndGetContext(ar);
					using (Stream stream = context.Response.OutputStream)
					{
						byte[] bytes = File.ReadAllBytes(filename);
						context.Response.ContentLength64 = bytes.Length;
						stream.Write(bytes, 0, bytes.Length);
						stream.Flush();
					}
				}, null);

				action(0);
			}
		}

		private static void GenerateFile(string filename)
		{
			int lines = 1024 * 6;
			using (TextWriter writer = File.CreateText(filename))
			{
				for (int i = 0; i < lines; i++)
				{
					int lineWidth = 128;
					string[] items = new string[lineWidth];
					for (int j = 0; j < lineWidth; j++)
					{
						items[j] = (j * i).ToString();
					}
					writer.WriteLine(string.Join(", ", items));
				}
				writer.Flush();
			}
		}
	}
}